package com.atjinggu.jinggumail.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atjinggu.jinggumail.common.to.es.SkuEsModule;
import com.atjinggu.jinggumail.common.utils.R;
import com.atjinggu.jinggumail.search.config.EsClientConfig;
import com.atjinggu.jinggumail.search.constant.EsConstant;
import com.atjinggu.jinggumail.search.feign.ProductFeignService;
import com.atjinggu.jinggumail.search.service.SearchService;
import com.atjinggu.jinggumail.search.vo.ResponseAttrVo;
import com.atjinggu.jinggumail.search.vo.SearchParamVo;
import com.atjinggu.jinggumail.search.vo.SearchResultVo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.ParsedAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author jingming
 * @Type SearchServiceImpl.java
 * @Desc
 * @date 2021/1/22 15:34
 */
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Autowired
    RestHighLevelClient client;

    @Autowired
    ProductFeignService productFeignService;

    @Override
    public SearchResultVo search(SearchParamVo paramVo) throws IOException {
        //构建查询
        SearchRequest request = buildSearchRequest(paramVo);
        SearchResponse result = client.search( request, EsClientConfig.COMMON_OPTIONS);
        SearchResultVo searchResultVo =   buildSearchResult(result,paramVo);
        return searchResultVo;
    }


    /**
     * 构建查询请求
     * @param paramVo
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParamVo paramVo) {
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //1.构建query
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1 构建关键字全文搜索
        if(StringUtils.isNotEmpty(paramVo.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", paramVo.getKeyword()));
        }
        //1.2 三级分类Id
        if(null != paramVo.getCatalog3Id()) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",paramVo.getCatalog3Id()));
        }
        //1.3 品牌Id
        if(null != paramVo.getBrandId()){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",paramVo.getBrandId()));
        }
        //1.4 属性信息 &attrs=attrId_attrValue1:attrValue1
        if(CollectionUtils.isNotEmpty(paramVo.getAttrs())){
            for (String attr : paramVo.getAttrs()) {
                String[] s = attr.split("_");
                String[] split = s[1].split(":");
                BoolQueryBuilder builder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("attrs.attrId", s[0])).must(QueryBuilders.termsQuery("attrs.attrValue", split));
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",builder, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }
        //1.5 价格信息skuPrice=200_500 /200_ / _500
        if(StringUtils.isNotEmpty(paramVo.getSkuPrice())){
            String[] s = paramVo.getSkuPrice().split("_");
            if(s.length == 2){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(s[0]).lte(s[1]));
            }else if(paramVo.getSkuPrice().startsWith("_")){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(s[0]));
            }else if(paramVo.getSkuPrice().endsWith("_")){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(s[0]));
            }
        }
        //1.6 只显示有货
        if(null != paramVo.getHasStock()) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", paramVo.getHasStock() == 1));
        }
        sourceBuilder.query(boolQueryBuilder);
        //1.7 排序信息 sort=saleCount_desc/asc
        if(StringUtils.isNotEmpty(paramVo.getSort())) {
            String[] s = paramVo.getSort().split("_");
            sourceBuilder.sort(s[0],s[1].equalsIgnoreCase("asc")? SortOrder.ASC:SortOrder.DESC);
        }
        //1.8 高亮
        if(StringUtils.isNotEmpty(paramVo.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }

       //1.9 分页
        sourceBuilder.from((paramVo.getPageNum() -1)*EsConstant.PRPDUCT_PAGE_SIZE);
        sourceBuilder.size(EsConstant.PRPDUCT_PAGE_SIZE);


        //2 构建聚合

        //2.1 构建品牌聚合
        TermsAggregationBuilder brand_aggs = AggregationBuilders.terms("brand_aggs").field("brandId").size(50);
        brand_aggs.subAggregation(AggregationBuilders.terms("brand_name_aggs").field("brandName").size(1));
        brand_aggs.subAggregation(AggregationBuilders.terms("brand_image_aggs").field("brandImg").size(1));
        sourceBuilder.aggregation(brand_aggs);
        //2.2 构建分类聚合
        TermsAggregationBuilder catalog_aggs = AggregationBuilders.terms("catalog_aggs").field("catalogId").size(50);
        catalog_aggs.subAggregation(AggregationBuilders.terms("catalog_name_aggs").field("catalogName").size(1));
        sourceBuilder.aggregation(catalog_aggs);
        //2.3 构建属性聚合
        NestedAggregationBuilder nested = AggregationBuilders.nested("attr_aggs", "attrs");
        TermsAggregationBuilder attr_id_aggs =    AggregationBuilders.terms("attr_id_aggs").field("attrs.attrId").size(50);
        nested.subAggregation(attr_id_aggs);
        attr_id_aggs.subAggregation(AggregationBuilders.terms("attr_name_aggs").field("attrs.attrName").size(50));
        attr_id_aggs.subAggregation(AggregationBuilders.terms("attr_value_aggs").field("attrs.attrValue").size(50));
        sourceBuilder.aggregation(nested);
        System.out.println("DSL语句："+sourceBuilder.toString());
        searchRequest.source(sourceBuilder);
        return searchRequest;
    }

    /**
     * 封装查询结果
     * @param result
     * @return
     */
    private SearchResultVo buildSearchResult(SearchResponse result,SearchParamVo paramVo) {
        SearchResultVo resultVo = new SearchResultVo();
        log.info("查询商品信息状态码 ************* "+result.status());
        if(result.status().equals(RestStatus.OK)) {
            //封装商品基本信息skuInfo
            SearchHits hit = result.getHits();
            SearchHit[] hits = hit.getHits();
            if(null != hits && hits.length > 0){
                List<SkuEsModule> skuInfo = Arrays.stream(hits).map(sku -> {
                    String sourceAsString = sku.getSourceAsString();
                    SkuEsModule module = JSON.parseObject(sourceAsString, SkuEsModule.class);
                    //设置高亮
                    if(StringUtils.isNotEmpty(paramVo.getKeyword())){
                        module.setSkuTitle(sku.getHighlightFields().get("skuTitle").getFragments()[0].toString());
                    }
                    return module;
                }).collect(Collectors.toList());
                resultVo.setSkuInfo(skuInfo);
            }

            Aggregations aggs = result.getAggregations();
            //封装商品聚合后的品牌信息
            ParsedAggregation brand_aggs = aggs.get("brand_aggs");
            List<? extends Terms.Bucket> buckets = ((ParsedLongTerms) brand_aggs).getBuckets();
            List<SearchResultVo.Brand> brands = buckets.stream().map(item -> {
                SearchResultVo.Brand brand = new SearchResultVo.Brand();
                Number keyAsNumber = item.getKeyAsNumber();
                brand.setBrandId(keyAsNumber.longValue());
                Aggregation brand_image_aggs = ((Terms.Bucket) item).getAggregations().get("brand_image_aggs");
                String image = ((ParsedStringTerms) brand_image_aggs).getBuckets().get(0).getKeyAsString();
                brand.setBrandImg(image);

                Aggregation brand_name_aggs = ((Terms.Bucket) item).getAggregations().get("brand_name_aggs");
                String name = ((ParsedStringTerms) brand_name_aggs).getBuckets().get(0).getKeyAsString();
                brand.setBrandName(name);

                return brand;
            }).collect(Collectors.toList());
            resultVo.setBrands(brands);

            //封装商品聚合后的分类信息
            ParsedAggregation catalog_aggs = aggs.get("catalog_aggs");
            List<? extends Terms.Bucket> cataLogs = ((ParsedLongTerms) catalog_aggs).getBuckets();
            List<SearchResultVo.CataLog> catalogs = cataLogs.stream().map(cataLog -> {
                SearchResultVo.CataLog cata = new SearchResultVo.CataLog();
                Number key = ((Terms.Bucket) cataLog).getKeyAsNumber();
                cata.setCatalogId(key.longValue());
                Aggregation catalog_name_aggs = ((Terms.Bucket) cataLog).getAggregations().get("catalog_name_aggs");
                List<? extends Terms.Bucket> b = ((ParsedStringTerms) catalog_name_aggs).getBuckets();
                String keyAsString = b.get(0).getKeyAsString();
                cata.setCatalogName(keyAsString);
                return cata;
            }).collect(Collectors.toList());
            resultVo.setCataLogs(catalogs);


            //封装属性信息
            ParsedAggregation attr_aggs = aggs.get("attr_aggs");
            Aggregations nestedAggs=  ((ParsedNested) attr_aggs).getAggregations();
            Aggregation attr_id_aggs = nestedAggs.get("attr_id_aggs");
            List<? extends Terms.Bucket> attrs = ((ParsedLongTerms) attr_id_aggs).getBuckets();
            List<SearchResultVo.Attr> collect = attrs.stream().map(item -> {
                SearchResultVo.Attr attr = new SearchResultVo.Attr();
                attr.setAttrId(((Terms.Bucket) item).getKeyAsNumber().longValue());
                Aggregation attr_name_aggs = ((Terms.Bucket) item).getAggregations().get("attr_name_aggs");
                List<? extends Terms.Bucket> b = ((ParsedStringTerms) attr_name_aggs).getBuckets();
                String keyAsString = b.get(0).getKeyAsString();
                attr.setAttrName(keyAsString);

                Aggregation attr_value_aggs = ((Terms.Bucket) item).getAggregations().get("attr_value_aggs");
                List<? extends Terms.Bucket> value = ((ParsedStringTerms) attr_value_aggs).getBuckets();
                List<String> attrValues = value.stream().map(is -> ((Terms.Bucket) is).getKeyAsString()).collect(Collectors.toList());
                attr.setAttrValue(attrValues);
                return attr;
            }).collect(Collectors.toList());
            resultVo.setAttrs(collect);

            //分页信息-页码
            resultVo.setPageNum(paramVo.getPageNum());
            long total = hit.getTotalHits().value;
            resultVo.setTotal(total);
            int totalPages = total % EsConstant.PRPDUCT_PAGE_SIZE ==0? (int) total / EsConstant.PRPDUCT_PAGE_SIZE:((int) total / EsConstant.PRPDUCT_PAGE_SIZE) + 1;
            resultVo.setTotalPages(totalPages);
            List<Integer> pageNavs = new ArrayList<>();
            IntStream.rangeClosed(1,totalPages).forEach(i -> {
                pageNavs.add(i);
            });
            resultVo.setPageNavs(pageNavs);

            if(CollectionUtils.isNotEmpty(paramVo.getAttrs())) {
                //构建面包屑导航vo
                List<SearchResultVo.NavVo> naVos = paramVo.getAttrs().stream().map(item -> {
                    SearchResultVo.NavVo navVo = new SearchResultVo.NavVo();
                    String[] s = item.split("_");
                    resultVo.getAttrIds().add(Long.parseLong(s[0]));
                    navVo.setNavValue(s[1]);
                    R attrInfo = productFeignService.getAttrInfo(Long.parseLong(s[0]));
                    if (attrInfo.getCode() == 0) {
                        ResponseAttrVo attr = attrInfo.getData("attr", new TypeReference<ResponseAttrVo>() {
                        });
                        navVo.setNavName(attr.getAttrName());
                    } else {
                        navVo.setNavName(s[0]);
                    }
                    String link = getLink(paramVo, item,"attrs");
                    navVo.setLink(link);
                    return navVo;
                }).collect(Collectors.toList());
                resultVo.setNavs(naVos);
            }

            //品牌上面包屑导航
            if(CollectionUtils.isNotEmpty(paramVo.getBrandId())){
                List<SearchResultVo.NavVo> navs = resultVo.getNavs();
                SearchResultVo.NavVo navVo = new SearchResultVo.NavVo();
                navVo.setNavName("品牌");
                navVo.setNavValue("xxx");
                String link = getLink(paramVo, paramVo.getBrandId().get(0)+"","brandId");
                navVo.setLink(link);
                navs.add(navVo);
            }
        }

        return resultVo;
    }

    private String getLink(SearchParamVo paramVo, String item,String key) {
        String encode = null;
        try {
            encode = URLEncoder.encode(item, "UTF-8");
            //java和浏览器对空格的编码不同，java时翻译成+，但是浏览器时%20
            encode= encode.replace("+","20%");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String string = paramVo.get_queryString().replace("&"+key+"="+encode, "").replace("?"+key+"="+encode,"");
        return "http://search.jinggumail.com/list.html?" + string;
    }

}